#pragma once

#include "DCoordinates.h"
#include "ParametricCurves.h"
#include "TriangulatedMeshes.h"
#include "Lights.h"
#include "ShaderPrograms.h"
#include "BicubicBezierPatch.h"
#include "BicubicUniformPeriodicBSplinePatch.h"
#include "Cylinder.h"
#include "Toroid.h"
#include "Sphere.h"
#include "Cameras.h"
#include <QGLWidget>
#include <QMouseEvent>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>

#define ONE_RADIAN 0.05
#define TWO_PI 6.283185307179586476925286766559
#define ROTATION_UNIT 0.02617993877991494365385536152733

class GLWidget: public QGLWidget
{
    Q_OBJECT

private:
    // parameters of the projection matrix
    float   _fovy, _aspect, _z_near, _z_far;

    // parameters of the model view matrix
    float   _eye[3], _center[3], _up[3];

    // transformation parameters
    int     _x_angle, _y_angle, _z_angle;
    float   _zoom;

    // shape
    cagd::ParametricCurve* _pc;

    // mouse
    cagd::TriangulatedMesh _mouse;

    // shader
    cagd::ShaderProgram _shader;

    // other
    QTimer* _timer;
    GLfloat _angle;

    // light
    cagd::DirectionalLight*                     _dl;
    cagd::PointLight*                           _pl;
    cagd::SpotLight*                            _sl;
    GLboolean                                   _dl_enabled;
    GLboolean                                   _pl_enabled;
    GLboolean                                   _sl_enabled;

    // camera
    cagd::CCamera                               _camera;

    // bicubic patch
    cagd::BicubicBezierPatch                    _patch;
    //cagd::BicubicUniformPeriodicBSplinePatch    _patch;
    cagd::TriangulatedMesh                      _before_interpolation, _after_interpolation;


    // a general surface on which the user should work
    cagd::BicubicUniformPeridocBSplineSurface*  _actual_surface;
    GLint                                       _actual_control_point_index;
    std::vector<cagd::DCoordinate>              _actula_control_points_pointer;
    cagd::DCoordinate                           _actual_control_point;

    GLboolean UpdateControlPointsPointer();
    GLboolean RenderAControlPoint();

    //textura
    GLuint                                      _texture;

    void mousePressEvent(QMouseEvent *);
    void mouseMoveEvent(QMouseEvent *);

public:
    // constructor
    GLWidget(QWidget* parent = 0);
    // destructor
    ~GLWidget();

    // overriden virtual methods (inherited from base class QGLWidget)
    void initializeGL();
    void paintGL();
    void resizeGL(int w, int h);

    void CreateAndReplaceActualSurfaceObject(GLuint type_index = 0,
                                             GLuint control_net_rows = 4,
                                             GLuint control_net_cols = 4,
                                             GLuint u_div_point_count = 3,
                                             GLuint v_div_point_count = 3,
                                             GLdouble param_0 = 1.0,
                                             GLdouble param_1 = 1.0,
                                             GLdouble param_2 = 1.0);

    void Walk(float step);
    void Strafe(float step);
    void Heighten(float step);
    void Roll(float step);
    void Pitch(float step);
    void Yaw(float step);

    void GetControlPointsInformation(GLuint &point_count, GLdouble &x, GLdouble &y, GLdouble &z);    
    GLboolean ControlPointManipulation_enable(bool enabled);
    void ControlPointIndex_changed(int index);

private slots:
    void animate();

public slots:
    void set_x_angle(int angle);
    void set_y_angle(int angle);
    void set_z_angle(int angle);
    void set_scale_factor(double zoom);

    void enable_directional_light(bool enabled);
    void enable_two_sided_lighting(bool enabled);
    void enable_toon_light(bool enabled);
    void enable_light_source_0(bool enabled);
    void enable_light_source_1(bool enabled);
    void enable_light_source_2(bool enabled);
    void ControlPointManipulation_accepted();
    void ControlPointManipulation_rejected();
    void ControlPoint_x_changed(double x);
    void ControlPoint_y_changed(double y);
    void ControlPoint_z_changed(double z);
    void LoadTexture_button();
};
